/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab-modules.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

//===========================================================================================
#include "Ogre_Gfx.h"

//===========================================================================================
dylab::Ogre_Gfx::Scene::Scene(const String & _name, Ogre_Gfx * _gfx)
: mGfx(_gfx), mCameraMap(true), mLightMap(true), mObjectMap(true), mObjectPoolMap(true)
, mEntityMap(true), mModelRendererMap(true), mRayQueryMap(true), mDefaultCamera(NULL)
{
    // check the argument
    DYLAB_ASSERT(mGfx);

    // create the scene manager
    mOgreSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC, _name);
    DYLAB_ASSERT(mOgreSceneMgr != NULL);

//    mOgreSceneMgr->setShowDebugShadows(true);
//    mOgreSceneMgr->setDisplaySceneNodes(true);
}
//-----------------------------------------------------------------------------
dylab::Ogre_Gfx::Scene::~Scene()
{
    mRayQueryMap.removeAll();
    mObjectMap.removeAll();
    mEntityMap.removeAll();
    mLightMap.removeAll();
    mCameraMap.removeAll();
}
//-----------------------------------------------------------------------------
dylab::String dylab::Ogre_Gfx::Scene::getName() const
{
    return mOgreSceneMgr->getName();
}
//-----------------------------------------------------------------------------
dylab::Ogre_Gfx::ICamera * 
dylab::Ogre_Gfx::Scene::createCamera(const String & _name)
{
    // create the camera
    Camera * camera = new Camera(_name, this);
    DYLAB_ASSERT(camera != NULL);

    // store the camera in the map
    mCameraMap.insert(_name, camera);

    // store the camera as the default, if it is the first one
    if (mDefaultCamera == NULL)
        mDefaultCamera = camera;

    // return the created camera
    return camera;
}
//-----------------------------------------------------------------------------
dylab::Ogre_Gfx::ICamera * 
dylab::Ogre_Gfx::Scene::getCamera(const String & _name)
{
    if (_name == DEFAULT_NAME)
    {
        DYLAB_ASSERT(mDefaultCamera != NULL);
        return mDefaultCamera;
    }
    else
        return mCameraMap.get(_name);
}
//-----------------------------------------------------------------------------
const dylab::Ogre_Gfx::ICamera * 
dylab::Ogre_Gfx::Scene::getCamera(const String & _name) const
{
    if (_name == DEFAULT_NAME)
    {
        DYLAB_ASSERT(mDefaultCamera != NULL);
        return mDefaultCamera;
    }
    else
        return mCameraMap.get(_name);
}
//-----------------------------------------------------------------------------
void dylab::Ogre_Gfx::Scene::destroyCamera(const String & _name)
{
    String name = _name;
    
    if (name == DEFAULT_NAME)
    {
        DYLAB_ASSERT(mDefaultCamera != NULL);
        name = mDefaultCamera->getOgreCamera()->getName();
    }

    mCameraMap.remove(name);
    mOgreSceneMgr->destroyCamera(name);
}
//-----------------------------------------------------------------------------
dylab::Ogre_Gfx::ILight * 
dylab::Ogre_Gfx::Scene::createLight(const String & _name, ILight::type_t _type)
{
    // create the light
    Light * light = new Light(_name, this, _type);
    DYLAB_ASSERT(light != NULL);

    // store the light in the map
    mLightMap.insert(_name, light);

    // return the created light
    return light;
}
//-----------------------------------------------------------------------------
dylab::Ogre_Gfx::ILight * 
dylab::Ogre_Gfx::Scene::getLight(const String & _name)
{
    return mLightMap.get(_name);
}
//-----------------------------------------------------------------------------
const dylab::Ogre_Gfx::ILight * 
dylab::Ogre_Gfx::Scene::getLight(const String & _name) const
{
    return mLightMap.get(_name);
}
//-----------------------------------------------------------------------------
void dylab::Ogre_Gfx::Scene::destroyLight(const String & _name)
{
    mLightMap.remove(_name);
    mOgreSceneMgr->destroyLight(_name);
}
//-----------------------------------------------------------------------------
dylab::Ogre_Gfx::IObject * 
dylab::Ogre_Gfx::Scene::createObject(const String & _name)
{
    // create the object
    Object * object = new Object(_name, this);
    DYLAB_ASSERT(object != NULL);

    // store the object in the map
    mObjectMap.insert(_name, object);

    // return the created object
    return object;
}
//-----------------------------------------------------------------------------
dylab::Ogre_Gfx::IObject * 
dylab::Ogre_Gfx::Scene::getObject(const String & _name)
{
    return mObjectMap.get(_name);
}
//-----------------------------------------------------------------------------
const dylab::Ogre_Gfx::IObject * 
dylab::Ogre_Gfx::Scene::getObject(const String & _name) const
{
    return mObjectMap.get(_name);
}
//-----------------------------------------------------------------------------
dylab::Ogre_Gfx::IObject * 
dylab::Ogre_Gfx::Scene::findObject(const String & _name)
{
    return mObjectMap.find(_name);
}
//-----------------------------------------------------------------------------
void dylab::Ogre_Gfx::Scene::destroyObject(const String & _name)
{
    mObjectMap.remove(_name);
    mOgreSceneMgr->destroyManualObject(_name);
}
//-----------------------------------------------------------------------------
dylab::Ogre_Gfx::IModelRenderer * 
dylab::Ogre_Gfx::Scene::createModelRenderer(const String & _name, const String & _materialName, SimObject * _parentSimObject)
{
    // create the modelGfx
    ModelRenderer * modelGfx = new ModelRenderer(_name, _materialName, _parentSimObject, this);
    DYLAB_ASSERT(modelGfx != NULL);

    // store the modelGfx in the map
    mModelRendererMap.insert(_name, modelGfx);

    // return the created modelGfx
    return modelGfx;
}
//-----------------------------------------------------------------------------
dylab::Ogre_Gfx::IModelRenderer * 
dylab::Ogre_Gfx::Scene::getModelRenderer(const String & _name)
{
    return mModelRendererMap.get(_name);
}
//-----------------------------------------------------------------------------
const dylab::Ogre_Gfx::IModelRenderer * 
dylab::Ogre_Gfx::Scene::getModelRenderer(const String & _name) const
{
    return mModelRendererMap.get(_name);
}
//-----------------------------------------------------------------------------
dylab::Ogre_Gfx::IModelRenderer * 
dylab::Ogre_Gfx::Scene::findModelRenderer(const String & _name)
{
    return mModelRendererMap.find(_name);
}
//-----------------------------------------------------------------------------
void dylab::Ogre_Gfx::Scene::destroyModelRenderer(const String & _name)
{
    mModelRendererMap.remove(_name);
    mOgreSceneMgr->destroyManualObject(_name);
}
//-----------------------------------------------------------------------------
dylab::Ogre_Gfx::IObjectPool * 
dylab::Ogre_Gfx::Scene::createObjectPool(const String & _name, uint32_t _maxObjects)
{
    // create the objectPool
    ObjectPool * objectPool = new ObjectPool(_name, this, _maxObjects);
    DYLAB_ASSERT(objectPool != NULL);

    // store the objectPool in the map
    mObjectPoolMap.insert(_name, objectPool);

    // return the created objectPool
    return objectPool;
}
//-----------------------------------------------------------------------------
dylab::Ogre_Gfx::IObjectPool * 
dylab::Ogre_Gfx::Scene::getObjectPool(const String & _name)
{
    return mObjectPoolMap.get(_name);
}
//-----------------------------------------------------------------------------
const dylab::Ogre_Gfx::IObjectPool * 
dylab::Ogre_Gfx::Scene::getObjectPool(const String & _name) const
{
    return mObjectPoolMap.get(_name);
}
//-----------------------------------------------------------------------------
void dylab::Ogre_Gfx::Scene::destroyObjectPool(const String & _name)
{
    mObjectPoolMap.remove(_name);
}
//-----------------------------------------------------------------------------
dylab::Ogre_Gfx::IEntity * 
dylab::Ogre_Gfx::Scene::createEntity(const String & _name, const String & _meshName)
{
    // create the entity
    Entity * entity = new Entity(_name, _meshName, this);
    DYLAB_ASSERT(entity != NULL);

    // store the entity in the map
    mEntityMap.insert(_name, entity);

    // return the created entity
    return entity;
}
//-----------------------------------------------------------------------------
dylab::Ogre_Gfx::IEntity * 
dylab::Ogre_Gfx::Scene::getEntity(const String & _name)
{
    return mEntityMap.get(_name);
}
//-----------------------------------------------------------------------------
const dylab::Ogre_Gfx::IEntity * 
dylab::Ogre_Gfx::Scene::getEntity(const String & _name) const
{
    return mEntityMap.get(_name);
}
//-----------------------------------------------------------------------------
void dylab::Ogre_Gfx::Scene::destroyEntity(const String & _name)
{
    mEntityMap.remove(_name);
    mOgreSceneMgr->destroyEntity(_name);
}
//-----------------------------------------------------------------------------
dylab::Ogre_Gfx::IRayQuery * 
dylab::Ogre_Gfx::Scene::createRayQuery(const String & _name, uint32_t _queryFlags, ICamera * _usedCamera, IViewport * _targetViewport)
{
    // determine the used camera
    ICamera * camera = _usedCamera;
    if (camera == NULL)
    {
        camera = mDefaultCamera;
        DYLAB_ASSERT(camera != NULL);        
    }

    // determine the target viewport
    IViewport * vp = _targetViewport;
    if (vp == NULL)
    {
        mGfx->getWindow()->getViewport();
        DYLAB_ASSERT(vp != NULL);
    }

    // create the ray query
    RayQuery * rayQuery = new RayQuery(_name, _queryFlags, this, dynamicCast<Viewport>(_targetViewport), dynamicCast<Camera>(_usedCamera));
    DYLAB_ASSERT(rayQuery != NULL);

    // store the ray into the map
    mRayQueryMap.insert(_name, rayQuery);

    // return the created rayQuery
    return rayQuery;
}
//-----------------------------------------------------------------------------
dylab::Ogre_Gfx::IRayQuery * 
dylab::Ogre_Gfx::Scene::getRayQuery(const String & _name)
{
    return mRayQueryMap.get(_name);
}
//-----------------------------------------------------------------------------
const dylab::Ogre_Gfx::IRayQuery * 
dylab::Ogre_Gfx::Scene::getRayQuery(const String & _name) const
{
    return mRayQueryMap.get(_name);
}
//-----------------------------------------------------------------------------
void dylab::Ogre_Gfx::Scene::destroyRayQuery(const String & _name)
{
    mOgreSceneMgr->destroyQuery(mRayQueryMap.get(_name)->getOgreRaySceneQuery());
    mRayQueryMap.remove(_name);    
}
//-----------------------------------------------------------------------------
void dylab::Ogre_Gfx::Scene::setAmbientLight(const Colour & _light)
{
    mOgreSceneMgr->setAmbientLight(toOgreColour(_light));
}
//-----------------------------------------------------------------------------
dylab::Colour dylab::Ogre_Gfx::Scene::getAmbientLight() const
{
    return toColour(mOgreSceneMgr->getAmbientLight());
}
//-----------------------------------------------------------------------------
void dylab::Ogre_Gfx::Scene::setShadowType(shadow_t _shadowType)
{
    switch (_shadowType)
    {
    case ST_NONE:
        mOgreSceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_NONE);
        break;
    case ST_STENCIL_ADDITIVE:
        mOgreSceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_STENCIL_ADDITIVE);
        break;
    case ST_STENCIL_MODULATIVE:
        mOgreSceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_STENCIL_MODULATIVE);
        break;
    case ST_TEXTURE_ADDITIVE:
        mOgreSceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_TEXTURE_ADDITIVE);
        break;
    case ST_TEXTURE_MODULATIVE:
        mOgreSceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_TEXTURE_MODULATIVE);
        break;
    }
}
//-----------------------------------------------------------------------------
dylab::Ogre_Gfx::Scene::shadow_t dylab::Ogre_Gfx::Scene::getShadowType() const
{
    switch (mOgreSceneMgr->getShadowTechnique())
    {
    case Ogre::SHADOWTYPE_NONE:
        return ST_NONE;
    case Ogre::SHADOWTYPE_STENCIL_ADDITIVE:
        return ST_STENCIL_ADDITIVE;
    case Ogre::SHADOWTYPE_STENCIL_MODULATIVE:
        return ST_STENCIL_MODULATIVE;
    case Ogre::SHADOWTYPE_TEXTURE_ADDITIVE:
        return ST_TEXTURE_ADDITIVE;
    case Ogre::SHADOWTYPE_TEXTURE_MODULATIVE:
        return ST_TEXTURE_MODULATIVE;
    default:
        throw EInvalidArgument(String::fmt("Unsupported shadow technique: %1%", mOgreSceneMgr->getShadowTechnique()));
    }
}
//-----------------------------------------------------------------------------
void dylab::Ogre_Gfx::Scene::showBoundingBoxes(bool _show)
{
    mOgreSceneMgr->showBoundingBoxes(_show);
}
//-----------------------------------------------------------------------------
bool dylab::Ogre_Gfx::Scene::areBoundingBoxesVisible() const
{
    return mOgreSceneMgr->getShowBoundingBoxes();
}
//-----------------------------------------------------------------------------
void dylab::Ogre_Gfx::Scene::prepareForRendering()
{
    for (ModelRendererMap::Iterator modelRenderer(mModelRendererMap); modelRenderer.isValid(); modelRenderer++)
        modelRenderer->prepareForRendering();
}
//-----------------------------------------------------------------------------